Add ZSTD bindings to the node:zlib package.#6007
Add ZSTD bindings to the node:zlib package.#6007alistairjevans wants to merge 10 commits intocloudflare:mainfrom
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
src/workerd/api/node/zlib-util.c++
Outdated
| ZstdEncoderContext::~ZstdEncoderContext() { | ||
| if (cctx_ != nullptr) { | ||
| ZSTD_freeCCtx(cctx_); | ||
| cctx_ = nullptr; |
There was a problem hiding this comment.
It might be worth wrapping this in a custom smart pointer to ensure the cleanup is correct. Non-blocking tho.
There was a problem hiding this comment.
Added (brotli implementation does already do this)
|
@anonrig ... given your familiarity with the zlib impl in workerd, it would be good to have your review on this as well. |
| constructor(options: ZstdOptions | undefined | null, mode: number) { | ||
| ok(mode === CONST_ZSTD_DECODE || mode === CONST_ZSTD_ENCODE); | ||
| zstdInitParamsArray.fill(-1); |
There was a problem hiding this comment.
This diverges from the Node.js implementation and makes it a lot less readable. Ideally the constructor should receive a parameter for initParamsArray just like Node.js
class Zstd extends ZlibBase {
constructor(opts, mode, initParamsArray, maxParam) {
If we diverge from Node.js, finding bugs & keep the code in sync would be a lot harder then it really is.
| size = "large", | ||
| src = "zstd-nodejs-test.wd-test", | ||
| args = ["--experimental"], | ||
| data = ["zstd-nodejs-test.js"], |
There was a problem hiding this comment.
| data = ["zstd-nodejs-test.js"], | |
| data = ["zlib-zstd-nodejs-test.js"], |
|
|
||
| wd_test( | ||
| size = "large", | ||
| src = "zstd-nodejs-test.wd-test", |
There was a problem hiding this comment.
| src = "zstd-nodejs-test.wd-test", | |
| src = "zlib-zstd-nodejs-test.wd-test", |
| export const zstdConstantsTest = { | ||
| test() { | ||
| // Flush directives | ||
| assert.strictEqual(typeof zlib.constants.ZSTD_e_continue, 'number'); |
There was a problem hiding this comment.
You've already tested this in zlib-nodejs-test.
|
LGTM. @alistairjevans formatter seems to be failing: https://github.com/cloudflare/workerd/actions/runs/21922721360/job/63307147452?pr=6007 @jasnell can you take a look? |
|
Thanks @anonrig, formatting fix pushed, linter passes locally now. |
|
Overall I think this looks good. What I'm considering... (something I wish I had thought to do on the previous |
|
@jasnell, would such a compatibility flag still allow us to opt-in to using the feature in Cloudflare Workers while we wait for the fuzz test to complete? |
|
@alistairjevans ... while the fuzz testing would happening (if we go that route) the compat flag would be marked as "experimental" which means only cloudflare accounts would be able to use it until that annotation is removed. Still need to decide tho if this is the path we'd want to take. Should have a decision soon. |
|
Let's not block - good idea to do as follow up - still want to see this land. |
jasnell
left a comment
There was a problem hiding this comment.
Overall, LGTM with a couple nits and a question about error handling that might need addressing before this lands. I'll do the sign off once the question is looked at :-) .. great job overall!!
There is a pre-existing issue we need to address with CompressionStream::write's validation of flush values but that's not introduced or made worse with this change (it's relevant for the brotli support also). And, there's missing external memory tracking here that can be done as a follow-up (see, for instance, the CompressionAllocator::AllocForBrotli, CompressionAllocator::FreeForZlib, etc). We'll want to come back to that.
| } | ||
|
|
||
| kj::Maybe<CompressionError> ZstdDecoderContext::resetStream() { | ||
| if (dctx_.get() != nullptr) { |
There was a problem hiding this comment.
Should this be clearing the error_ state? e.g. if work() fails, then resetStream() is called, then work() succeeds, will getError() be returning the right thing?
| } | ||
|
|
||
| kj::Maybe<CompressionError> ZstdDecoderContext::setParams(int key, int value) { | ||
| size_t result = ZSTD_DCtx_setParameter(dctx_.get(), static_cast<ZSTD_dParameter>(key), value); |
There was a problem hiding this comment.
Nit: should there be a KJ_DASSERT to verify dctx_.get() is valid?
| // For decompression, lastResult == 0 means frame is complete | ||
| // If we have flush_ == ZSTD_e_end equivalent and there's input left, that's an error | ||
| if (flush_ == ZSTD_e_end && input_.pos < input_.size && lastResult == 0) { | ||
| // Frame completed but there's still input - could be trailing data |
There was a problem hiding this comment.
Nit: what is this if block for if it's empty?
Based pretty heavily on the brotli support.
Closes #4013